Amazon ConnectとLexを活用し、自動ヒアリングした内容をAmazon Bedrockで要約してメール通知してみた
Amazon Connect アドベントカレンダー 2024、2日目の記事です!
クラスメソッドとギークフィードさん、クラウドビルダーズさん、ネットプロテクションズさん、AWSJさんの有志が募ってチャレンジしている企画になります。
(アドベントカレンダーのカレンダー一覧はこちら↓)
はじめに
本記事では、Amazon ConnectとAmazon Lexを活用し、自動ヒアリングした内容をAmazon Bedrockで要約し、メール通知する方法について解説します。
この実装によるユーザー体験の流れは、以下の通りです。
ステップ | 実行者 | アクション |
---|---|---|
1 | ユーザー | 電話をかける |
2 | Connect | 「申し訳ございません。ただいま電話に出ることができません。メッセージを録音してよければ、はい、とお伝えください。」 |
3 | ユーザー | 「はい」と応答 |
4 | Connect(Lex) | 「ご要件、お名前、住所の3点をお伝えください。無音の状態が一定時間ある場合、録音が切れますので、ご了承ください。」 |
5 | ユーザー | 必要事項を伝える |
6 | Lambda | 録音内容をBedrockで要約し、メール通知する |
7 | Connect | 「以上で録音を終了します。電話を切ります。」 |
以下に構成図を示します。
前提条件
- SNSトピック作成済み
- Connectインスタンスは作成済み
- LexとConnectフローの構築、およびLexの音声入力時間の上限緩和については、以下の参考記事をもとに対応済みとします。
Lex
作成済みのLexボットのスロットfree
に以下のプロンプトを修正します。
修正前
お問い合わせ内容をお伝え下さい。無音の状態が一定時間ある場合、録音が切れますので、ご了承ください。
修正後
ご要件、お名前、住所の3点をお伝え下さい。無音の状態が一定時間ある場合、録音が切れますので、ご了承ください。
Lambda
以下の設定でLambdaを作成します。
- ランタイム: Python 3.13
- タイムアウト: 20秒
- IAMロールに付与するポリシー
- AWSLambdaBasicExecutionRole
- AmazonBedrockFullAccess
- AmazonSNSFullAccess
- メモリ:512MB
- メモリは512MBの方が実行完了時間が短かったため、512MBを選択しています。
- メモリ128MBの場合、4~6秒
- メモリ512MBの場合、3~4秒
- メモリは512MBの方が実行完了時間が短かったため、512MBを選択しています。
import json
import boto3
import datetime
from zoneinfo import ZoneInfo
MODEL_ID = 'anthropic.claude-3-5-sonnet-20240620-v1:0'
TOPIC_ARN = 'arn:aws:sns:ap-northeast-1:012345678901:xxxx'
def invoke_bedrock(prompt):
client = boto3.client('bedrock-runtime')
response = client.converse(
modelId=MODEL_ID,
messages=[{
"role": "user",
"content": [{
"text": prompt
}]
}],
inferenceConfig={
"maxTokens": 1000,
"temperature": 0
}
)
response_text = response['output']['message']['content'][0]['text']
print('Received response_text:' + json.dumps(response_text, ensure_ascii=False))
return response_text
def get_contact_time():
jst_now = datetime.datetime.now(ZoneInfo("Asia/Tokyo"))
return jst_now.strftime('%Y-%m-%d %H:%M:%S')
def create_message(contact_time, phone_number, formatted_text, original_text):
return f"""お問い合わせ情報
・日時: {contact_time}
・電話番号: {phone_number}
・お問い合わせ内容(整形済み文章)
{formatted_text}
・お問い合わせ内容(未整形文章)
{original_text}"""
def lambda_handler(event, context):
print('Received event:' + json.dumps(event, ensure_ascii=False))
recording = event['Details']['ContactData']['Attributes']['recording']
phone_number = event['Details']['ContactData']['SystemEndpoint']['Address']
contact_time = get_contact_time()
prompt = f"""
以下の音声文字起こしテキストから、ご要件、名前、住所の3つの情報を抽出し、以下のフォーマットで整理してください:
ご要件:[内容を自然な日本語で記述]
お名前:[名前を記述]
ご住所:[住所を記述]
なお、情報が不足している場合は「情報なし」と記載してください。
余計な説明は不要です。上記フォーマットのみを返してください。
テキスト:
{recording}
"""
formatted_text = invoke_bedrock(prompt)
message = create_message(contact_time, phone_number, formatted_text, recording)
sns = boto3.client('sns')
sns.publish(
TopicArn=TOPIC_ARN,
Message=message,
Subject='新規お問い合わせ通知'
)
return {
'statusCode': 200,
'body': json.dumps({
'formatted_text': formatted_text,
'message': 'Successfully processed contact flow event'
}, ensure_ascii=False)
}
処理の概要は以下のとおりです。
- Lambda関数がLexの文字起こしデータ(
recording
)と発信元電話番号を取得します。 - 現在時刻を日本標準時(JST)で取得し、整形します。
- 文字起こしデータをもとに、生成AIモデル(Anthropic Claude)に「ご要件」「お名前」「ご住所」を抽出してもらいます。
- 整形済みテキストを取得し、SNS通知用のメッセージを作成し、メール送信します。
Connectフロー
Connectフローでは、以下の2つのブロックを追加します。
Connectフローは、Lambdaとコンタクト属性の設定を追加します。
今回は、Lambdaの呼び出しタイムアウトは1秒に設定しています。
LambdaからConnectへのReturnは不要であり、Lambdaの実行完了をまで待つ時間が数秒かかるため、省略します。
Lambdaの呼び出しがタイムアウトになってもSNSでメール通知の処理はされます。ただし。ConnectのフローログではLambdaのエラーと出力されます。フローログのエラーが気になる場合、タイムアウトを最大の8秒にしましょう。
{
"Results": "The Lambda Function Returned An Error.",
"ContactId": "9cbe3e4c-9a2f-4239-a887-41f1169e8123",
"Parameters": {
"FunctionArn": "arn:aws:lambda:ap-northeast-1:012345678901:function:voicemail-to-email-notifier",
"ResponseValidation": "ResponseType=STRING_MAP",
"TimeLimit": "1000"
}
~中略~
}
作成したConnectフローのjsonファイル(クリックで展開)
{
"Version": "2019-10-30",
"StartAction": "17a389ed-1002-4550-9bbf-efbbe3811cf9",
"Metadata": {
"entryPointPosition": {
"x": 36.8,
"y": 65.6
},
"ActionMetadata": {
"1990a178-5825-414e-bd24-2bc3dc5dc937": {
"position": {
"x": 357.6,
"y": 61.6
},
"children": [
"d6ed2b04-80f5-4018-8262-87d58b7f0cb7"
],
"overrideConsoleVoice": true,
"fragments": {
"SetContactData": "d6ed2b04-80f5-4018-8262-87d58b7f0cb7"
},
"overrideLanguageAttribute": true
},
"d6ed2b04-80f5-4018-8262-87d58b7f0cb7": {
"position": {
"x": 357.6,
"y": 61.6
},
"dynamicParams": []
},
"17a389ed-1002-4550-9bbf-efbbe3811cf9": {
"position": {
"x": 140.8,
"y": 60.8
}
},
"465002a2-1f89-476b-8f58-e0e2671632c7": {
"position": {
"x": 820.8,
"y": 271.2
}
},
"1a41bbb2-6b65-4bf0-871b-deaf288ba7a1": {
"position": {
"x": 819.2,
"y": 460.8
}
},
"0cebb197-9920-4fcc-8410-a2f9e9fb12ff": {
"position": {
"x": 572,
"y": 57.6
},
"parameters": {
"LexV2Bot": {
"AliasArn": {
"displayName": "TestBotAlias",
"useLexBotDropdown": true,
"lexV2BotName": "cm-hirai-beyond-15seconds"
}
}
},
"dynamicMetadata": {
"x-amz-lex:audio:start-timeout-ms:*:*": false,
"x-amz-lex:audio:end-timeout-ms:*:*": false,
"x-amz-lex:audio:max-length-ms:*:*": false
},
"useLexBotDropdown": true,
"lexV2BotName": "cm-hirai-beyond-15seconds",
"lexV2BotAliasName": "TestBotAlias",
"conditionMetadata": [
{
"id": "7582d5d9-aaed-4540-b12d-92c9fa587fed",
"operator": {
"name": "Equals",
"value": "Equals",
"shortDisplay": "="
},
"value": "recording"
}
]
},
"acf5bea6-926a-42f6-a9c7-2f9cf4af97ef": {
"position": {
"x": 819.2,
"y": 60
},
"parameters": {
"Attributes": {
"recording": {
"useDynamic": true
}
}
},
"dynamicParams": [
"recording"
]
},
"3c2cba41-f8a9-418f-b428-c573abb3c010": {
"position": {
"x": 1257.6,
"y": 296
}
},
"87155153-6569-4cbc-8796-80b66eee6955": {
"position": {
"x": 1035.2,
"y": 60.8
}
},
"3297b9ca-f7e5-4822-9697-2a06b6b322e3": {
"position": {
"x": 1245.6,
"y": 56.8
},
"parameters": {
"LambdaFunctionARN": {
"displayName": "voicemail-to-email-notifier"
}
},
"dynamicMetadata": {}
}
},
"Annotations": [],
"name": "cm-hirai-voicemail-to-email-notifier",
"description": "",
"type": "contactFlow",
"status": "published",
"hash": {}
},
"Actions": [
{
"Parameters": {
"TextToSpeechEngine": "Neural",
"TextToSpeechStyle": "None",
"TextToSpeechVoice": "Kazuha"
},
"Identifier": "1990a178-5825-414e-bd24-2bc3dc5dc937",
"Type": "UpdateContactTextToSpeechVoice",
"Transitions": {
"NextAction": "d6ed2b04-80f5-4018-8262-87d58b7f0cb7"
}
},
{
"Parameters": {
"LanguageCode": "ja-JP"
},
"Identifier": "d6ed2b04-80f5-4018-8262-87d58b7f0cb7",
"Type": "UpdateContactData",
"Transitions": {
"NextAction": "0cebb197-9920-4fcc-8410-a2f9e9fb12ff",
"Errors": [
{
"NextAction": "0cebb197-9920-4fcc-8410-a2f9e9fb12ff",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {
"FlowLoggingBehavior": "Enabled"
},
"Identifier": "17a389ed-1002-4550-9bbf-efbbe3811cf9",
"Type": "UpdateFlowLoggingBehavior",
"Transitions": {
"NextAction": "1990a178-5825-414e-bd24-2bc3dc5dc937"
}
},
{
"Parameters": {
"Text": "録音されないということで承知しました。電話を切ります。"
},
"Identifier": "465002a2-1f89-476b-8f58-e0e2671632c7",
"Type": "MessageParticipant",
"Transitions": {
"NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
"Errors": [
{
"NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {
"Text": "エラーとなりました。"
},
"Identifier": "1a41bbb2-6b65-4bf0-871b-deaf288ba7a1",
"Type": "MessageParticipant",
"Transitions": {
"NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
"Errors": [
{
"NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {
"Text": "申し訳ございません。ただいま電話に出ることができません。メッセージを録音してよければ、はい、とお伝え下さい。",
"LexSessionAttributes": {
"x-amz-lex:audio:start-timeout-ms:*:*": "8000",
"x-amz-lex:audio:end-timeout-ms:*:*": "4000",
"x-amz-lex:audio:max-length-ms:*:*": "50000"
},
"LexV2Bot": {
"AliasArn": "arn:aws:lex:ap-northeast-1:012345678901:bot-alias/OMRBM5O69M/TSTALIASID"
}
},
"Identifier": "0cebb197-9920-4fcc-8410-a2f9e9fb12ff",
"Type": "ConnectParticipantWithLexBot",
"Transitions": {
"NextAction": "1a41bbb2-6b65-4bf0-871b-deaf288ba7a1",
"Conditions": [
{
"NextAction": "acf5bea6-926a-42f6-a9c7-2f9cf4af97ef",
"Condition": {
"Operator": "Equals",
"Operands": [
"recording"
]
}
}
],
"Errors": [
{
"NextAction": "465002a2-1f89-476b-8f58-e0e2671632c7",
"ErrorType": "NoMatchingCondition"
},
{
"NextAction": "1a41bbb2-6b65-4bf0-871b-deaf288ba7a1",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {
"Attributes": {
"recording": "$.Lex.Slots.free"
},
"TargetContact": "Current"
},
"Identifier": "acf5bea6-926a-42f6-a9c7-2f9cf4af97ef",
"Type": "UpdateContactAttributes",
"Transitions": {
"NextAction": "87155153-6569-4cbc-8796-80b66eee6955",
"Errors": [
{
"NextAction": "87155153-6569-4cbc-8796-80b66eee6955",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {},
"Identifier": "3c2cba41-f8a9-418f-b428-c573abb3c010",
"Type": "DisconnectParticipant",
"Transitions": {}
},
{
"Parameters": {
"Text": "以上で録音を終了します。電話を切ります。"
},
"Identifier": "87155153-6569-4cbc-8796-80b66eee6955",
"Type": "MessageParticipant",
"Transitions": {
"NextAction": "3297b9ca-f7e5-4822-9697-2a06b6b322e3",
"Errors": [
{
"NextAction": "3297b9ca-f7e5-4822-9697-2a06b6b322e3",
"ErrorType": "NoMatchingError"
}
]
}
},
{
"Parameters": {
"LambdaFunctionARN": "arn:aws:lambda:ap-northeast-1:012345678901:function:voicemail-to-email-notifier",
"InvocationTimeLimitSeconds": "1",
"ResponseValidation": {
"ResponseType": "STRING_MAP"
}
},
"Identifier": "3297b9ca-f7e5-4822-9697-2a06b6b322e3",
"Type": "InvokeLambdaFunction",
"Transitions": {
"NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
"Errors": [
{
"NextAction": "3c2cba41-f8a9-418f-b428-c573abb3c010",
"ErrorType": "NoMatchingError"
}
]
}
}
]
}
試してみた
いくつかのサンプル発話を用意し、それぞれのメール通知内容を確認してみます。
サンプル1
発話内容は、以下のとおりです。住所を発話しないパターンです。
もしもし、クラスメソッドと申します。
先日、購入した洗濯機の調子が悪くて連絡しました。型番はAWAの2000です。
昨日の夜から、運転すると異常な振動と大きな音がするようになりました。脱水時に特にひどくなります。
説明書を確認して、設置の水平度合いを確認したり、洗濯物の偏りがないか調整しましたが、改善されません。
おそらく保証期間内なので、修理をお願いしたいのですが、明日か明後日に来ていただくことは可能でしょうか?
私の連絡先は090-1234-5678です。午前中であれば在宅しております。
お手数ですが、ご連絡いただければ幸いです。よろしくお願いいたします。
メール本文は以下の通りでした。
お問い合わせ情報
・日時: 2024-11-20 15:48:16
・電話番号: +81xxxxxxxxxx
・お問い合わせ内容(整形済み文章)
ご要件:先日購入した洗濯機(型番AWA2000)の調子が悪く、昨日の夜から運転時に異常な振動と大きな音がする。特に脱水時に顕著。保証期間内なので修理を依頼したい。明日か明後日の訪問を希望。
お名前:クラスメソット
ご住所:情報なし
・お問い合わせ内容(未整形文章)
もしもし クラス メソット と 申し ます 先日 購入 し た 洗濯 機 の 調子 が 悪く て 連絡 し まし た 型番 は a w a の 二 千 です 昨日 の 夜 から 運転 する と 異常 な 新郎 と 大きな 音 が する よう に なり まし た 脱水 時 に 特に し とく なり ます説明 書 を 確認 し て 設置 の 水平 度合い を 確認 し たり 洗濯 物 の 偏り が ない か 調整 し まし た が 改善 さ れ ませ んおそらく 保証 期間 内 な の で 修理 を お 願い し たい の です が 明日 か 明後日 日経 化学 こと は 可能 でしょう か 私 の 連絡 先 は 〇 九 〇 一 五 三 四 五 六 七 八 です お 膳 中 で あれ ば 在宅 し て おり ます お 手数 です が 連絡 いただけ ます と 幸い です よろしく お 願い いたし ます
文字起こしされた文章は、Bedrockによって句読点やスペースが削除されており、読みやすく整形されています。
要約に連絡先(090-1234-5678)も入っているとよりよいですね。
住所がない場合は、「情報なし」と正しく記載されています。
サンプル2
発話内容は、以下のとおりです。住所の情報が2箇所に散らばっています。
東京都港区西新橋1-1-1 に住んでいる佐藤太郎と申します。
先日メールでもお問い合わせしたんですが、返事がなくて、注文した商品がまだ届かないので確認したくて電話しました。
注文番号はわかりません。
住所は先ほど言った通りなんですが、マンション名を言い忘れました。日比谷フォートタワー26階です。
実は先週の水曜日に注文したはずなんですが、確認メールも来てなくて心配で。
普段は2,3日で届くと聞いていたので、もしかしたら注文が正しく完了していないのかなと思って。
商品は確か合計で12,800円分注文したんですが、クレジットカードの引き落としも確認できていません。
メール本文は以下の通りでした。
お問い合わせ情報
・日時: 2024-11-20 16:18:55
・電話番号: +81xxxxxxxxxx
・お問い合わせ内容(整形済み文章)
ご要件:先週水曜日に注文した商品(口切り1万800円分)がまだ届かず、確認メールも来ていないため、注文状況を確認したい。
お名前:サトウ タロウ
ご住所:東京都港区西新橋1-1-1 日比谷ポートタワー 16階
・お問い合わせ内容(未整形文章)
東京 都 港 区 西新橋 一 の 一 の 一 に 住ん で いる サトウ タロウ と 申し ます 先日 メール で も 問い合わせ し た ん です が 返事 が なく て 注文 し た 商品 が まだ 届か ない の で 確認 し たく て 電話 し まし た 注文 番号 は わかり ませ ん 住所 は 先程 言っ た とおり なんて な ん です が マンション 名 を 言い 忘れ まし た 日比谷 ポート タワー に 十 六 回 です 実 は 先週 の 水曜 日 に 注文 し た ん です が 確認 メール も 来 なく て 心配 で 普段 は 三 三 に し て 届く と 聞い て い た の で もし か し たら 注文 が 探し 完了 し て い ない の か な と 思っ て 商品 は 確か 口 切り 一 万 に し て 八百 円 分 注文 し た ん です が クレジット カード の 引き落とし も 確認 でき て い ませ ん
問い合わせ内容では住所の情報が散らばっていますが、問題なく抽出されています。
住所を文字起こしが正しくないため、整形後の(口切り1万800円分)
も誤りがあります。
サンプル3
発話内容は、以下のとおりです。住所を発話しないパターンです。
商品のことでお電話したんですけど。
カタログで見た青い花瓶がすごく気に入って、在庫を確認したいです。
鈴木と申します。
その青い花瓶なんですが、サイズが3種類あると書いてあって、一番大きいサイズだと置く場所に困るかもしれないので、実際のサイズ感を教えて頂きたいです。
あと、もし可能でしたら、実物の写真とかありますでしょうか?
カタログの写真だと、色味がちょっとわかりにくくて。
ネットでも探してみたんですが、詳しい情報が見つけられませんでした
メール本文は以下の通りでした。
お問い合わせ情報
・日時: 2024-11-20 16:25:24
・電話番号: +81xxxxxxxxxx
・お問い合わせ内容(整形済み文章)
ご要件:カタログで見た青い花瓶の在庫確認、サイズ感の詳細、実物の写真の有無を問い合わせたい。
お名前:スズキ
ご住所:情報なし
・お問い合わせ内容(未整形文章)
商品 の こと で お 電話 し た ん です けど カタログ で 見 た 青い 過敏 書 すごく 気 に 入っ て 在庫 を 確認 し たい です スズキ と 申し ます その 青い 過敏 な ん です が サイズ が 三 種類 ある と 書い て あっ て 一番 大きい サイズ だ と 置く 場所 に も 困る か も しれ ない の で 実際 の サイズ 感 教え て いただき たい です あと もし 可能 でし たら 実物 の 写真 と か あり ます でしょう か カタログ の 写真 だ と 色 味 が ちょっと わかり にくく て ネット で も 見 た ん です が 詳しい 情報 が 見つけ られ ませ ん でし た
住所がない場合は、「情報なし」と正しく記載されています。
サンプル4
発話内容は、以下のとおりです。要件が複数あるパターンです。
田中美咲です。
北海道札幌市中央区北3条西1-1-1 札幌ブリックキューブ10階に住んでいます。
2点ほど確認したくて電話させていただきました。
先日注文した商品の配送日の変更をしたいです。できれば来週の水曜日に配送をお願いしたいのですが、大丈夫ですか?
あと、新しく出た商品カタログを送っていただきたいんです。先月お願いしたんですが、まだ届いていないです。
以前使っていた商品がとても良かったので、新商品も気になっているんです。
特に美容液のシリーズが気になっていて、新しいラインナップを確認したいので、できるだけ早めに送っていただけると嬉しいです。
メール本文は以下の通りでした。
お問い合わせ情報
・日時: 2024-11-20 16:31:49
・電話番号: +81xxxxxxxxxx
・お問い合わせ内容(整形済み文章)
ご要件:先日注文した商品の配送日を来週の水曜日に変更したい。新しく出た商品カタログを送ってほしい。特に美容液のシリーズの新しいラインナップを確認したい。
お名前:タナカ ミサキ
ご住所:北海道札幌市中央区北三条西一の一の一 札幌ブリック九日十階
・お問い合わせ内容(未整形文章)
タナカ ミサ キ です 北海道 札幌 市 中央 区 北 三条 西 一 の 一 の 一 札幌 ブリック 九 日 十 階 に 住ん で い ます 三 点 ほど 確認 し たく て 電話 し て いただけ まし た 先日 注文 し た 商品 の 配送 日 の 変更 を し たい です できれ ば 来週 の 水曜 日 に 配送 お 願い し たい の です が 大丈夫 です か あと は 新しく 出 た 商品 カタログ を 送っ て いただき たい ん です 先日 お 願い し た ん です が まだ 届い て い ない です 以前 使っ て い た 商品 が とても よかっ た の で 新 商品 も 気 に なっ て いる ん です 特に 美容 液 の シリーズ は 気 に なっ て い て 新しい ラインナップ を 確認 し たい の で できる だけ 早め に 送っ て いただける と 嬉しい です
要件が複数あるパターンですが、正しく要件がまとめられています。
住所は誤った文字起こしになっていますので、整形後も誤っています。
最後に
本記事では、Amazon Connect、Amazon Lex、Amazon Bedrockを組み合わせた自動ヒアリング内容の要約とメール通知の仕組みを解説しました。
サンプルを通じて、実装の流れや精度を確認し、実用性を検証しました。
この仕組みは、留守番電話や簡易電話代行の代替などで活用できる可能性があり、顧客対応の効率化や業務の自動化に大きく貢献します。ぜひ、自社のニーズに合わせて検証・活用してみてください。